Skip to content

feat: add deep-link support and Raycast extension#1708

Open
Angelebeats wants to merge 11 commits intoCapSoftware:mainfrom
Angelebeats:main
Open

feat: add deep-link support and Raycast extension#1708
Angelebeats wants to merge 11 commits intoCapSoftware:mainfrom
Angelebeats:main

Conversation

@Angelebeats
Copy link
Copy Markdown

@Angelebeats Angelebeats commented Apr 2, 2026

This PR adds deep-link support to Cap (cap://record, cap://stop, cap://pause) and includes a dedicated Raycast extension for remote control.

Greptile Summary

This PR adds three new deep-link actions (StartDefaultRecording, PauseRecording, ResumeRecording) and a new cap:// URL scheme to complement the existing cap-desktop:// scheme, enabling Raycast and other external tools to remotely control Cap's recording lifecycle via cap://record, cap://stop, cap://pause, and cap://resume.

Key issues found:

  • P0 – Wrong event payload in StartDefaultRecording: app.emit("request-open-recording-picker", ()) emits a JSON null payload, but the RequestOpenRecordingPicker tauri_specta struct expects { "target_mode": null }. Every other call site in hotkeys.rs correctly uses RequestOpenRecordingPicker { target_mode: None }.emit(&app). This mismatch will cause the frontend event listener to receive an unexpected payload, likely causing cap://record to silently do nothing.
  • P2 – Generic cap:// scheme expands the attack surface: Registering the short, guessable cap scheme system-wide means any local application or browser link (e.g., a <a href="cap://record"> on a web page) can invisibly trigger screen recording. The existing cap-desktop://action?value=... format was harder to exploit accidentally; the new URLs remove that friction.

Confidence Score: 2/5

  • Not safe to merge — the cap://record action will silently fail due to a payload type mismatch in the emitted event.
  • The StartDefaultRecording handler emits the request-open-recording-picker event with a () (unit/null) payload instead of the typed RequestOpenRecordingPicker { target_mode: None } struct used everywhere else in the codebase. This payload mismatch means the primary advertised feature (cap://record opening the recording picker) will not function correctly. The fix is straightforward but the bug must be resolved before merging.
  • apps/desktop/src-tauri/src/deeplink_actions.rs — specifically the StartDefaultRecording arm in the execute match.

Important Files Changed

Filename Overview
apps/desktop/src-tauri/src/deeplink_actions.rs Adds StartDefaultRecording, PauseRecording, and ResumeRecording variants and a cap:// URL parsing branch; contains a P0 event payload bug — app.emit("request-open-recording-picker", ()) emits null instead of the expected { target_mode: null }, causing frontend deserialization failure. Also bypasses the typed tauri_specta event system used everywhere else in the codebase.
apps/desktop/src-tauri/tauri.conf.json Adds "cap" alongside "cap-desktop" in the deep-link scheme registration; mechanically correct but introduces a short, guessable system-wide scheme.

Sequence Diagram

sequenceDiagram
    participant Raycast as Raycast or Browser
    participant OS as OS Deep-Link Router
    participant Tauri as Tauri Plugin
    participant Handler as deeplink_actions
    participant Recording as recording module
    participant Frontend as Frontend Webview

    Raycast->>OS: "open cap://record"
    OS->>Tauri: "cap scheme triggered"
    Tauri->>Handler: "urls: [cap://record]"
    Handler->>Handler: "parse: scheme==cap, host==record"
    Handler->>Handler: "Ok(StartDefaultRecording)"
    Handler->>Frontend: "app.emit(request-open-recording-picker, null) ⚠️"
    Note over Frontend: "Expected payload: {target_mode:null}, got: null"

    Raycast->>OS: "open cap://stop"
    OS->>Tauri: "cap scheme triggered"
    Tauri->>Handler: "urls: [cap://stop]"
    Handler->>Handler: "Ok(StopRecording)"
    Handler->>Recording: "stop_recording(app, state)"

    Raycast->>OS: "open cap://pause"
    OS->>Tauri: "cap scheme triggered"
    Tauri->>Handler: "urls: [cap://pause]"
    Handler->>Handler: "Ok(PauseRecording)"
    Handler->>Recording: "pause_recording(app, state)"

    Raycast->>OS: "open cap://resume"
    OS->>Tauri: "cap scheme triggered"
    Tauri->>Handler: "urls: [cap://resume]"
    Handler->>Handler: "Ok(ResumeRecording)"
    Handler->>Recording: "resume_recording(app, state)"
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 169-171

Comment:
**Wrong event payload causes frontend deserialization failure**

`app.emit("request-open-recording-picker", ())` sends a JSON `null` payload, but the `RequestOpenRecordingPicker` struct has a `target_mode: Option<RecordingTargetMode>` field. The auto-generated frontend type expects `{ target_mode: null }`, not `null`. This payload mismatch will likely cause the event to silently fail or be ignored by the frontend listener.

All other call sites in `hotkeys.rs` correctly use the typed `tauri_specta` event. Use the same pattern here:

```suggestion
            DeepLinkAction::StartDefaultRecording => {
                RequestOpenRecordingPicker { target_mode: None }.emit(app).map_err(|e| e.to_string())
            }
```

You'll also need to import `RequestOpenRecordingPicker` at the top of the file (or reference it as `crate::RequestOpenRecordingPicker`).

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 94-102

Comment:
**Security: generic `cap://` scheme is accessible by any app or web page**

Registering `cap` as a system-wide URL scheme means any application — including browsers that silently follow `cap://` links in web pages — can trigger `cap://record`, `cap://stop`, `cap://pause`, and `cap://resume` without user confirmation. A malicious web page could embed `<a href="cap://record">` or automatically redirect to it, invisibly starting a screen capture session.

The existing `cap-desktop://action?value=...` scheme has the same surface area, but its JSON-in-query-string format is significantly harder to invoke accidentally or from a web page. The new `cap://` URLs reduce the exploit complexity to a single, guessable link.

Consider requiring explicit user-facing confirmation (e.g., showing a toast/alert before acting), or at minimum documenting this behavior so users are aware that any local app can control their recording session via these URLs.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat: implement cap:// deep-link protoco..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

.map_err(|_| ActionParseFromUrlError::Invalid);
}

if url.scheme() == "cap" {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Host matching is currently case-sensitive, so cap://Stop etc would be rejected. Might be worth using eq_ignore_ascii_case here for resilience.

Suggested change
if url.scheme() == "cap" {
if url.scheme() == "cap" {
return match url.host_str() {
Some(host) if host.eq_ignore_ascii_case("record") => Ok(Self::StartDefaultRecording),
Some(host) if host.eq_ignore_ascii_case("stop") => Ok(Self::StopRecording),
Some(host) if host.eq_ignore_ascii_case("pause") => Ok(Self::PauseRecording),
Some(host) if host.eq_ignore_ascii_case("resume") => Ok(Self::ResumeRecording),
_ => Err(ActionParseFromUrlError::Invalid),
};
}

crate::show_window(app.clone(), ShowCapWindow::Settings { page }).await
}
DeepLinkAction::StartDefaultRecording => {
app.emit("request-open-recording-picker", ()).map_err(|e| e.to_string())
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StartDefaultRecording currently emits a stringly-typed event and (from the name) reads like it should start immediately. If the intent is to open the picker, using the existing typed event keeps emit patterns consistent.

Suggested change
app.emit("request-open-recording-picker", ()).map_err(|e| e.to_string())
DeepLinkAction::StartDefaultRecording => {
crate::RequestOpenRecordingPicker { target_mode: None }
.emit(app)
.map_err(|e| e.to_string())
}

Comment on lines +169 to +171
DeepLinkAction::StartDefaultRecording => {
app.emit("request-open-recording-picker", ()).map_err(|e| e.to_string())
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Wrong event payload causes frontend deserialization failure

app.emit("request-open-recording-picker", ()) sends a JSON null payload, but the RequestOpenRecordingPicker struct has a target_mode: Option<RecordingTargetMode> field. The auto-generated frontend type expects { target_mode: null }, not null. This payload mismatch will likely cause the event to silently fail or be ignored by the frontend listener.

All other call sites in hotkeys.rs correctly use the typed tauri_specta event. Use the same pattern here:

Suggested change
DeepLinkAction::StartDefaultRecording => {
app.emit("request-open-recording-picker", ()).map_err(|e| e.to_string())
}
DeepLinkAction::StartDefaultRecording => {
RequestOpenRecordingPicker { target_mode: None }.emit(app).map_err(|e| e.to_string())
}

You'll also need to import RequestOpenRecordingPicker at the top of the file (or reference it as crate::RequestOpenRecordingPicker).

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 169-171

Comment:
**Wrong event payload causes frontend deserialization failure**

`app.emit("request-open-recording-picker", ())` sends a JSON `null` payload, but the `RequestOpenRecordingPicker` struct has a `target_mode: Option<RecordingTargetMode>` field. The auto-generated frontend type expects `{ target_mode: null }`, not `null`. This payload mismatch will likely cause the event to silently fail or be ignored by the frontend listener.

All other call sites in `hotkeys.rs` correctly use the typed `tauri_specta` event. Use the same pattern here:

```suggestion
            DeepLinkAction::StartDefaultRecording => {
                RequestOpenRecordingPicker { target_mode: None }.emit(app).map_err(|e| e.to_string())
            }
```

You'll also need to import `RequestOpenRecordingPicker` at the top of the file (or reference it as `crate::RequestOpenRecordingPicker`).

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +94 to +102
if url.scheme() == "cap" {
return match url.host_str() {
Some("record") => Ok(Self::StartDefaultRecording),
Some("stop") => Ok(Self::StopRecording),
Some("pause") => Ok(Self::PauseRecording),
Some("resume") => Ok(Self::ResumeRecording),
_ => Err(ActionParseFromUrlError::Invalid),
};
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Security: generic cap:// scheme is accessible by any app or web page

Registering cap as a system-wide URL scheme means any application — including browsers that silently follow cap:// links in web pages — can trigger cap://record, cap://stop, cap://pause, and cap://resume without user confirmation. A malicious web page could embed <a href="cap://record"> or automatically redirect to it, invisibly starting a screen capture session.

The existing cap-desktop://action?value=... scheme has the same surface area, but its JSON-in-query-string format is significantly harder to invoke accidentally or from a web page. The new cap:// URLs reduce the exploit complexity to a single, guessable link.

Consider requiring explicit user-facing confirmation (e.g., showing a toast/alert before acting), or at minimum documenting this behavior so users are aware that any local app can control their recording session via these URLs.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 94-102

Comment:
**Security: generic `cap://` scheme is accessible by any app or web page**

Registering `cap` as a system-wide URL scheme means any application — including browsers that silently follow `cap://` links in web pages — can trigger `cap://record`, `cap://stop`, `cap://pause`, and `cap://resume` without user confirmation. A malicious web page could embed `<a href="cap://record">` or automatically redirect to it, invisibly starting a screen capture session.

The existing `cap-desktop://action?value=...` scheme has the same surface area, but its JSON-in-query-string format is significantly harder to invoke accidentally or from a web page. The new `cap://` URLs reduce the exploit complexity to a single, guessable link.

Consider requiring explicit user-facing confirmation (e.g., showing a toast/alert before acting), or at minimum documenting this behavior so users are aware that any local app can control their recording session via these URLs.

How can I resolve this? If you propose a fix, please make it concise.

.map_err(|_| ActionParseFromUrlError::Invalid);
}

if url.scheme().eq_ignore_ascii_case("cap") {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This currently accepts any path after the host (e.g. cap://record/anything). If the intent is to support only the exact host actions, consider rejecting non-root paths to avoid surprising matches.

Suggested change
if url.scheme().eq_ignore_ascii_case("cap") {
if url.scheme().eq_ignore_ascii_case("cap") {
if url.path() != "/" {
return Err(ActionParseFromUrlError::Invalid);
}
return match url.host_str() {
Some(h) if h.eq_ignore_ascii_case("record") => Ok(Self::StartDefaultRecording),
Some(h) if h.eq_ignore_ascii_case("stop") => Ok(Self::StopRecording),
Some(h) if h.eq_ignore_ascii_case("pause") => Ok(Self::PauseRecording),
Some(h) if h.eq_ignore_ascii_case("resume") => Ok(Self::ResumeRecording),
_ => Err(ActionParseFromUrlError::Invalid),
};
}

| DeepLinkAction::StartDefaultRecording
| DeepLinkAction::PauseRecording
| DeepLinkAction::ResumeRecording => {
crate::notifications::NotificationType::DeepLinkTriggered.send(app);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NotificationType::DeepLinkTriggered is gated by enable_notifications (see send_notification). If this is meant as a security heads-up, users who disabled notifications won’t get any signal that a deep-link triggered recording actions. Might be worth using an always-on in-app toast for deep links, or bypassing that setting just for this case.

let enable_notifications = GeneralSettingsStore::get(app)
.map(|settings| settings.is_some_and(|s| s.enable_notifications))
.unwrap_or(false);
pub fn send_notification(app: &tauri::AppHandle, notification_type: NotificationType, always: bool) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing send_notification to require always is a breaking API change — any existing send_notification(app, ty) call sites will stop compiling. Might be cleaner to keep the 2-arg send_notification and add a separate send_notification_always (or a private inner helper) for the bypass.

return match url.host_str() {
Some(h) if h.eq_ignore_ascii_case("record") => Ok(Self::StartDefaultRecording),
Some(h) if h.eq_ignore_ascii_case("stop") => Ok(Self::StopRecording),
Some(h) if h.eq_ignore_ascii_case("pause") => Ok(Self::TogglePauseRecording),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: cap://pause maps to TogglePauseRecording, but the enum still has PauseRecording (and an execute arm). If it’s not used by the JSON deep-link path, consider dropping it to reduce confusion / surface area.

| DeepLinkAction::StartDefaultRecording
| DeepLinkAction::ResumeRecording
| DeepLinkAction::TogglePauseRecording => {
crate::notifications::NotificationType::DeepLinkTriggered.send_always(app);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will fire an always-on OS notification for every deep-link action (including pause/resume toggles), which feels like it could get pretty noisy for Raycast/automation and also bypasses the user's notification preference. Consider only forcing the notification for actions that start recording, and let the others respect the setting.

Suggested change
crate::notifications::NotificationType::DeepLinkTriggered.send_always(app);
match &self {
DeepLinkAction::StartRecording { .. } | DeepLinkAction::StartDefaultRecording => {
crate::notifications::NotificationType::DeepLinkTriggered.send_always(app);
}
DeepLinkAction::StopRecording
| DeepLinkAction::ResumeRecording
| DeepLinkAction::TogglePauseRecording => {
crate::notifications::NotificationType::DeepLinkTriggered.send(app);
}
_ => {}
}

return Err(ActionParseFromUrlError::Invalid);
}

return match url.host_str().map(|h| h.to_lowercase().as_str()) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

url.host_str().map(|h| h.to_lowercase().as_str()) returns an &str pointing at a temporary String (won’t compile). Matching on host_str() directly avoids the allocation and fixes the lifetime issue.

Suggested change
return match url.host_str().map(|h| h.to_lowercase().as_str()) {
if scheme == "cap" {
let path = url.path().trim_matches("/");
if !path.is_empty() {
return Err(ActionParseFromUrlError::Invalid);
}
return match url.host_str() {
Some(host) if host.eq_ignore_ascii_case("record") => Ok(Self::StartDefaultRecording),
Some(host) if host.eq_ignore_ascii_case("stop") => Ok(Self::StopRecording),
Some(host) if host.eq_ignore_ascii_case("pause") => Ok(Self::TogglePauseRecording),
Some(host) if host.eq_ignore_ascii_case("resume") => Ok(Self::ResumeRecording),
_ => Err(ActionParseFromUrlError::Invalid),
};
}

@Angelebeats
Copy link
Copy Markdown
Author

Refined the deep-link implementation based on the feedback:

  • Security Notifications: Only actions that start a recording now force a security notification (DeepLinkTriggered.send_always). Stop, Pause, and Resume actions now use a standard notification that respects the user's preference settings to reduce noise.
  • Host Parsing: Fixed the case-sensitivity and lifetime issues in URL host parsing by matching on owned Strings.
  • Payload Consistency: Verified that the event emitted for StartDefaultRecording uses the typed tauri-specta struct, ensuring the frontend receives the expected payload { target_mode: null } instead of a raw null.

Please let me know if there are any other nits or logic gaps to address!

return Err(ActionParseFromUrlError::Invalid);
}

return match url.host_str().map(|h| h.to_lowercase()) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor tweak: url.host_str().map(|h| h.to_lowercase()) allocates for every deep link. You can keep this allocation-free and a bit clearer with eq_ignore_ascii_case:

Suggested change
return match url.host_str().map(|h| h.to_lowercase()) {
return match url.host_str() {
Some(h) if h.eq_ignore_ascii_case("record") => Ok(Self::StartDefaultRecording),
Some(h) if h.eq_ignore_ascii_case("stop") => Ok(Self::StopRecording),
Some(h) if h.eq_ignore_ascii_case("pause") => Ok(Self::TogglePauseRecording),
Some(h) if h.eq_ignore_ascii_case("resume") => Ok(Self::ResumeRecording),
_ => Err(ActionParseFromUrlError::Invalid),
};


impl DeepLinkAction {
pub async fn execute(self, app: &AppHandle) -> Result<(), String> {
// Handle security/visibility notification for deep link actions
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repo style note: we try to avoid inline code comments. Here there are a few // ... lines (path check + notification rationale + payload note). I’d drop the comments and let naming / structure carry the intent (or extract the deep-link notification into a small helper).

"deep-link": {
"desktop": {
"schemes": ["cap-desktop"]
"schemes": ["cap-desktop", "cap"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registering the short cap scheme makes it very easy for other local apps (and sometimes browsers) to trigger recording actions. Since this expands the surface area, consider making the deep-link notification unconditionally visible for all deep-link actions (not just start), or gating deep-link control behind an explicit user setting.

@Angelebeats
Copy link
Copy Markdown
Author

Latest round of refinements implemented to meet the project's industrial-grade standards:

  • Performance: Replaced .to_lowercase() with eq_ignore_ascii_case during host parsing to eliminate unnecessary allocations.
  • Security: As requested, deep-link notifications are now unconditionally visible for all actions (start, stop, pause, resume) to prevent any potential silent remote control.
  • Repo Style: Removed all redundant inline comments and cleaned up the parsing logic.
  • Enum Sync: Confirmed that only TogglePauseRecording is used for the deep-link path to keep the implementation lean.

Ready for final review and merge! @tembo-bot please check the new logic.

Comment on lines 49 to 54
ActionParseFromUrlError::ParseFailed(msg) => {
eprintln!("Failed to parse deep link \"{}\": {}", &url, msg)
error!("Failed to parse deep link \"{}\": {}", &url, msg)
}
ActionParseFromUrlError::Invalid => {
eprintln!("Invalid deep link format \"{}\"", &url)
warn!("Invalid deep link format \"{}\"", &url)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These logs can include the full deep-link URL (including the value= query payload). Consider stripping query/fragment before logging to avoid leaking potentially sensitive data.

Suggested change
ActionParseFromUrlError::ParseFailed(msg) => {
eprintln!("Failed to parse deep link \"{}\": {}", &url, msg)
error!("Failed to parse deep link \"{}\": {}", &url, msg)
}
ActionParseFromUrlError::Invalid => {
eprintln!("Invalid deep link format \"{}\"", &url)
warn!("Invalid deep link format \"{}\"", &url)
}
ActionParseFromUrlError::ParseFailed(msg) => {
let mut redacted = url.clone();
redacted.set_query(None);
redacted.set_fragment(None);
error!("Failed to parse deep link \"{}\": {}", redacted, msg)
}
ActionParseFromUrlError::Invalid => {
let mut redacted = url.clone();
redacted.set_query(None);
redacted.set_fragment(None);
warn!("Invalid deep link format \"{}\"", redacted)
}

use std::path::{Path, PathBuf};
use tauri::{AppHandle, Manager, Url};
use tracing::trace;
use tracing::{trace, warn, error};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: trace is imported but unused now; dropping it avoids warnings.

Suggested change
use tracing::{trace, warn, error};
use tracing::{error, warn};

ScreenshotCopiedToClipboard,
ScreenshotSaveFailed,
ScreenshotCopyFailed,
DeepLinkTriggered,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since DeepLinkTriggered is sent via send_always (bypassing user notification preference), consider also suppressing the notification sound for it so other apps can’t spam audio via deep links.

Suggested change
DeepLinkTriggered,
let skip_sound = matches!(
notification_type,
NotificationType::DeepLinkTriggered
| NotificationType::ScreenshotSaved
| NotificationType::ScreenshotCopiedToClipboard
| NotificationType::ScreenshotSaveFailed
| NotificationType::ScreenshotCopyFailed
);

.map_err(|_| ActionParseFromUrlError::Invalid);
}

if scheme == "cap" {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cap://record works, but some launchers produce cap:///record (no host, path-only). Accepting a single path segment as a fallback makes this a bit more robust without changing behavior for normal URLs.

Suggested change
if scheme == "cap" {
if scheme == "cap" {
let path = url.path().trim_matches('/');
let (action, extra) = match (url.host_str(), path) {
(Some(host), "") => (host, ""),
(Some(_), _) => return Err(ActionParseFromUrlError::Invalid),
(None, "") => return Err(ActionParseFromUrlError::Invalid),
(None, path) => {
let mut parts = path.split('/');
(parts.next().unwrap_or(""), parts.next().unwrap_or(""))
}
};
if !extra.is_empty() {
return Err(ActionParseFromUrlError::Invalid);
}
return match action {
host if host.eq_ignore_ascii_case("record") => Ok(Self::StartDefaultRecording),
host if host.eq_ignore_ascii_case("stop") => Ok(Self::StopRecording),
host if host.eq_ignore_ascii_case("pause") => Ok(Self::TogglePauseRecording),
host if host.eq_ignore_ascii_case("resume") => Ok(Self::ResumeRecording),
_ => Err(ActionParseFromUrlError::Invalid),
};
}

crate::recording::resume_recording(app.clone(), app.state()).await
}
DeepLinkAction::TogglePauseRecording => {
crate::recording::toggle_pause_recording(app.clone(), app.state()).await
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cap://pause currently maps to TogglePauseRecording, so sending pause twice will resume. If the intent is separate pause/resume deep links, this arm probably wants to pause only.

Suggested change
crate::recording::toggle_pause_recording(app.clone(), app.state()).await
DeepLinkAction::TogglePauseRecording => {
crate::recording::pause_recording(app.clone(), app.state()).await
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant